import axios from 'axios'
import querystring from 'querystring'
import { URL } from 'url'
import OAuthFailed from "../error/OAuthFailed.mjs";

export default class AppPixivAPI {
  /**
   * @type string
   */
  hosts

  /**
   * @type string
   */
  accessToken

  /**
   * @type {AxiosInstance}
   */
  axios

  /**
   * Initialize the Pixiv API client
   * @param {string} accessToken 令牌
   * @param {Object} requestsKwargs - Optional Axios configuration
   */
  constructor(accessToken, requestsKwargs = {}) {
    this.hosts = 'https://app-api.pixiv.net';
    this.accessToken = accessToken;
    this.axios = axios.create(requestsKwargs);
  }

  /**
   * Set proxy hosts
   * @param {string} proxyHosts - Proxy host URL (e.g., "http://app-api.pixivlite.com")
   */
  setApiProxy(proxyHosts) {
    this.hosts = proxyHosts;
  }


  /**
   * 配置accessToken
   * @param {string} accessToken - Proxy host URL (e.g., "http://app-api.pixivlite.com")
   */
  setAccessToken(accessToken) {
    this.accessToken = accessToken;
  }

  /**
   * Make an API request
   * @private
   */
  async _request(method, url, headers = {}, params = null, data = null, reqAuth = true) {
    const headers_ = { ...headers };
    
    if (this.hosts !== "https://app-api.pixiv.net") {
      headers_['host'] = "app-api.pixiv.net";
    }
    
    if (!headers_['user-agent']) {
      headers_['app-os'] = "ios";
      headers_['app-os-version'] = "14.6";
      headers_['user-agent'] = "PixivIOSApp/7.13.3 (iOS 14.6; iPhone13,2)";
    }
    
    if (reqAuth) {
      this._requireAuth();
      headers_['Authorization'] = `Bearer ${this.accessToken}`;
    }
    
    const config = {
      method,
      url,
      headers: headers_,
      params,
      data,
    };
    
    try {
      const response = await this.axios(config);
      return response.data;
    } catch (error) {
      if (error.response?.data?.error?.message?.startsWith("Error occurred at the OAuth process")) {
        throw new OAuthFailed(error.message)
      } else {
        throw new Error(error.message)
      }
    }
  }

  /**
   * Ensure authentication is set up
   * @private
   */
  _requireAuth() {
    if (!this.accessToken) {
      throw new Error('Authentication required! Please set access token first.');
    }
  }

  /**
   * Format boolean values for API
   * @private
   */
  _formatBool(boolValue) {
    if (typeof boolValue === 'boolean') {
      return boolValue ? 'true' : 'false';
    }
    if (boolValue === 'true' || boolValue === 'True') {
      return 'true';
    }
    return 'false';
  }

  /**
   * Format date for API
   * @private
   */
  _formatDate(date) {
    if (date instanceof Date) {
      return date.toISOString().split('T')[0];
    }
    // Validate string format (YYYY-MM-DD)
    if (typeof date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(date)) {
      return date;
    }
    throw new Error('Invalid date format. Expected YYYY-MM-DD or Date object.');
  }

  /**
   * 返回翻页用参数
   * @private
   */
  parseQs(nextUrl) {
    if (!nextUrl) return null;
    
    const resultQs = {};
    const query = new URL(nextUrl).searchParams;
    
    query.forEach((value, key) => {
      // Handle array parameters
      if (key.includes('[') && key.endsWith(']')) {
        const baseKey = key.split('[')[0];
        if (!resultQs[baseKey]) {
          resultQs[baseKey] = [];
        }
        resultQs[baseKey].push(value);
      } else {
        resultQs[key] = value;
      }
    });
    
    return resultQs;
  }

  // User API Methods

  /**
   * 用户详情
   * @param userId
   * @param filter
   * @param reqAuth
   * @return {Promise<AppUserDetailApiResponse|undefined>}
   */
  async userDetail(userId, filter = "for_ios", reqAuth = true) {
    const url = `${this.hosts}/v1/user/detail`;
    const params = {
      user_id: userId,
      filter,
    };
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  /**
   * 用户作品列表
   * @param userId
   * @param type
   * @param filter
   * @param offset
   * @param reqAuth
   * @return {Promise<*|undefined>}
   */
  async userIllusts(userId, type = "illust", filter = "for_ios", offset = null, reqAuth = true) {
    const url = `${this.hosts}/v1/user/illusts`;
    const params = {
      user_id: userId,
      filter,
    };
    if (type) params.type = type;
    if (offset) params.offset = offset;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  /**
   * 用户收藏作品列表
   * @param userId
   * @param restrict
   * @param filter
   * @param maxBookmarkId
   * @param tag
   * @param reqAuth
   * @return {Promise<*|undefined>}
   */
  async userBookmarksIllust(userId, restrict = "public", filter = "for_ios", maxBookmarkId = null, tag = null, reqAuth = true) {
    const url = `${this.hosts}/v1/user/bookmarks/illust`;
    const params = {
      user_id: userId,
      restrict,
      filter,
    };
    if (maxBookmarkId) params.max_bookmark_id = maxBookmarkId;
    if (tag) params.tag = tag;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  /**
   * 用户收藏小说列表
   * @param userId
   * @param restrict
   * @param filter
   * @param maxBookmarkId
   * @param tag 从 user_bookmark_tags_illust 获取的收藏标签
   * @param reqAuth
   * @return {Promise<*|undefined>}
   */
  async userBookmarksNovel(userId, restrict = "public", filter = "for_ios", maxBookmarkId = null, tag = null, reqAuth = true) {
    const url = `${this.hosts}/v1/user/bookmarks/novel`;
    const params = {
      user_id: userId,
      restrict,
      filter,
    };
    if (maxBookmarkId) params.max_bookmark_id = maxBookmarkId;
    if (tag) params.tag = tag;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  /**
   *
   * @param seedUserId
   * @param filter
   * @param offset
   * @param reqAuth
   * @return {Promise<*|undefined>}
   */
  async userRelated(seedUserId, filter = "for_ios", offset = null, reqAuth = true) {
    const url = `${this.hosts}/v1/user/related`;
    const params = {
      filter,
      offset: offset || 0,
      seed_user_id: seedUserId,
    };
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  async userRecommended(filter = "for_ios", offset = null, reqAuth = true) {
    const url = `${this.hosts}/v1/user/recommended`;
    const params = { filter };
    if (offset) params.offset = offset;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  /**
   * 关注用户的新作
   * @param restrict {'public' | 'private'}
   * @param offset
   * @param reqAuth
   * @return {Promise<*|undefined>}
   */
  async illustFollow(restrict = "public", offset = null, reqAuth = true) {
    const url = `${this.hosts}/v2/illust/follow`;
    const params = { restrict };
    if (offset) params.offset = offset;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  // Illust API Methods

  /**
   * 作品详情 (类似PAPI.works(),iOS中未使用)
   * @param illustId
   * @param reqAuth
   * @return {Promise<AppIllustDetail|undefined>}
   */
  async illustDetail(illustId, reqAuth = true) {
    const url = `${this.hosts}/v1/illust/detail`;
    const params = { illust_id: illustId };
    const response = await this._request('GET', url, {}, params, null, reqAuth);
    return response.illust
  }

  /**
   * 作品评论
   * @param illustId
   * @param offset
   * @param includeTotalComments
   * @param reqAuth
   * @return {Promise<*|undefined>}
   */
  async illustComments(illustId, offset = null, includeTotalComments = null, reqAuth = true) {
    const url = `${this.hosts}/v1/illust/comments`;
    const params = { illust_id: illustId };
    if (offset) params.offset = offset;
    if (includeTotalComments) params.include_total_comments = this._formatBool(includeTotalComments);
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  /**
   * 相关作品列表
   * @param illustId
   * @param filter
   * @param seedIllustIds
   * @param offset
   * @param viewed
   * @param reqAuth
   * @return {Promise<*|undefined>}
   */
  async illustRelated(illustId, filter = "for_ios", seedIllustIds = null, offset = null, viewed = null, reqAuth = true) {
    const url = `${this.hosts}/v2/illust/related`;
    const params = {
      illust_id: illustId,
      filter,
    };
    if (offset) params.offset = offset;
    if (seedIllustIds) {
      params['seed_illust_ids[]'] = Array.isArray(seedIllustIds) ? seedIllustIds : [seedIllustIds];
    }
    if (viewed) {
      params['viewed[]'] = Array.isArray(viewed) ? viewed : [viewed];
    }
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  /**
   * 插画推荐 (Home - Main)
   * @param contentType
   * @param includeRankingLabel
   * @param filter
   * @param maxBookmarkIdForRecommend
   * @param minBookmarkIdForRecentIllust
   * @param offset
   * @param includeRankingIllusts
   * @param bookmarkIllustIds
   * @param includePrivacyPolicy
   * @param viewed
   * @param reqAuth
   * @return {Promise<*|undefined>}
   */
  async illustRecommended(contentType = "illust", includeRankingLabel = true, filter = "for_ios", 
                         maxBookmarkIdForRecommend = null, minBookmarkIdForRecentIllust = null, 
                         offset = null, includeRankingIllusts = null, bookmarkIllustIds = null, 
                         includePrivacyPolicy = null, viewed = null, reqAuth = true) {
    const url = reqAuth 
      ? `${this.hosts}/v1/illust/recommended`
      : `${this.hosts}/v1/illust/recommended-nologin`;
    
    const params = {
      content_type: contentType,
      include_ranking_label: this._formatBool(includeRankingLabel),
      filter,
    };
    
    if (maxBookmarkIdForRecommend) params.max_bookmark_id_for_recommend = maxBookmarkIdForRecommend;
    if (minBookmarkIdForRecentIllust) params.min_bookmark_id_for_recent_illust = minBookmarkIdForRecentIllust;
    if (offset) params.offset = offset;
    if (includeRankingIllusts) params.include_ranking_illusts = this._formatBool(includeRankingIllusts);
    
    if (viewed) {
      params['viewed[]'] = Array.isArray(viewed) ? viewed : [viewed];
    }
    
    if (!reqAuth && bookmarkIllustIds) {
      params.bookmark_illust_ids = Array.isArray(bookmarkIllustIds) 
        ? bookmarkIllustIds.join(',') 
        : bookmarkIllustIds;
    }
    
    if (includePrivacyPolicy) params.include_privacy_policy = includePrivacyPolicy;
    
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  /**
   *
   * @param mode
   * @param filter
   * @param date
   * @param offset
   * @param reqAuth
   * @return {Promise<*|undefined>}
   */
  async illustRanking(mode = "day", filter = "for_ios", date = null, offset = null, reqAuth = true) {
    const url = `${this.hosts}/v1/illust/ranking`;
    const params = {
      mode,
      filter,
    };
    if (date) params.date = this._formatDate(date);
    if (offset) params.offset = offset;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  /**
   * 趋势标签 (Search - tags)
   * @param filter
   * @param reqAuth
   * @return {Promise<*|undefined>}
   */
  async trendingTagsIllust(filter = "for_ios", reqAuth = true) {
    const url = `${this.hosts}/v1/trending-tags/illust`;
    const params = { filter };
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  // Search API Methods

  async searchIllust(word, searchTarget = "partial_match_for_tags", sort = "date_desc", 
                     duration = null, startDate = null, endDate = null, 
                     filter = "for_ios", searchAiType = null, offset = null, reqAuth = true) {
    const url = `${this.hosts}/v1/search/illust`;
    const params = {
      word,
      search_target: searchTarget,
      sort,
      filter,
    };
    if (startDate) params.start_date = this._formatDate(startDate);
    if (endDate) params.end_date = this._formatDate(endDate);
    if (duration) params.duration = duration;
    if (searchAiType) params.search_ai_type = searchAiType;
    if (offset) params.offset = offset;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  async searchNovel(word, searchTarget = "partial_match_for_tags", sort = "date_desc", 
                    mergePlainKeywordResults = "true", includeTranslatedTagResults = "true", 
                    startDate = null, endDate = null, filter = null, 
                    searchAiType = null, offset = null, reqAuth = true) {
    const url = `${this.hosts}/v1/search/novel`;
    const params = {
      word,
      search_target: searchTarget,
      merge_plain_keyword_results: mergePlainKeywordResults,
      include_translated_tag_results: includeTranslatedTagResults,
      sort,
    };
    if (filter) params.filter = filter;
    if (startDate) params.start_date = this._formatDate(startDate);
    if (endDate) params.end_date = this._formatDate(endDate);
    if (searchAiType) params.search_ai_type = searchAiType;
    if (offset) params.offset = offset;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  async searchUser(word, sort = "date_desc", duration = null, filter = "for_ios", offset = null, reqAuth = true) {
    const url = `${this.hosts}/v1/search/user`;
    const params = {
      word,
      sort,
      filter,
    };
    if (duration) params.duration = duration;
    if (offset) params.offset = offset;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  // Bookmark API Methods

  async illustBookmarkDetail(illustId, reqAuth = true) {
    const url = `${this.hosts}/v2/illust/bookmark/detail`;
    const params = { illust_id: illustId };
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  async illustBookmarkAdd(illustId, restrict = "public", tags = null, reqAuth = true) {
    const url = `${this.hosts}/v2/illust/bookmark/add`;
    const data = {
      illust_id: illustId,
      restrict,
    };
    if (tags) {
      data['tags[]'] = Array.isArray(tags) ? tags.join(' ') : tags;
    }
    return this._request('POST', url, {}, null, querystring.stringify(data), reqAuth);
  }

  async illustBookmarkDelete(illustId, reqAuth = true) {
    const url = `${this.hosts}/v1/illust/bookmark/delete`;
    const data = { illust_id: illustId };
    return this._request('POST', url, {}, null, querystring.stringify(data), reqAuth);
  }

  // Follow API Methods

  async userFollowAdd(userId, restrict = "public", reqAuth = true) {
    const url = `${this.hosts}/v1/user/follow/add`;
    const data = {
      user_id: userId,
      restrict,
    };
    return this._request('POST', url, {}, null, querystring.stringify(data), reqAuth);
  }

  async userFollowDelete(userId, reqAuth = true) {
    const url = `${this.hosts}/v1/user/follow/delete`;
    const data = { user_id: userId };
    return this._request('POST', url, {}, null, querystring.stringify(data), reqAuth);
  }

  async userEditAiShowSettings(setting, reqAuth = true) {
    const url = `${this.hosts}/v1/user/ai-show-settings/edit`;
    const data = { show_ai: setting };
    return this._request('POST', url, {}, null, querystring.stringify(data), reqAuth);
  }

  async userBookmarkTagsIllust(userId, restrict = "public", offset = null, reqAuth = true) {
    const url = `${this.hosts}/v1/user/bookmark-tags/illust`;
    const params = {
      user_id: userId,
      restrict,
    };
    if (offset) params.offset = offset;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  async userFollowing(userId, restrict = "public", offset = null, reqAuth = true) {
    const url = `${this.hosts}/v1/user/following`;
    const params = {
      user_id: userId,
      restrict,
    };
    if (offset) params.offset = offset;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  async userFollower(userId, filter = "for_ios", offset = null, reqAuth = true) {
    const url = `${this.hosts}/v1/user/follower`;
    const params = {
      user_id: userId,
      filter,
    };
    if (offset) params.offset = offset;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  async userMypixiv(userId, offset = null, reqAuth = true) {
    const url = `${this.hosts}/v1/user/mypixiv`;
    const params = { user_id: userId };
    if (offset) params.offset = offset;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  async userList(userId, filter = "for_ios", offset = null, reqAuth = true) {
    const url = `${this.hosts}/v2/user/list`;
    const params = {
      user_id: userId,
      filter,
    };
    if (offset) params.offset = offset;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  // Ugoira API Methods

  async ugoiraMetadata(illustId, reqAuth = true) {
    const url = `${this.hosts}/v1/ugoira/metadata`;
    const params = { illust_id: illustId };
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  // Novel API Methods

  async userNovels(userId, filter = "for_ios", offset = null, reqAuth = true) {
    const url = `${this.hosts}/v1/user/novels`;
    const params = {
      user_id: userId,
      filter,
    };
    if (offset) params.offset = offset;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  async novelSeries(seriesId, filter = "for_ios", lastOrder = null, reqAuth = true) {
    const url = `${this.hosts}/v2/novel/series`;
    const params = {
      series_id: seriesId,
      filter,
    };
    if (lastOrder) params.last_order = lastOrder;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  async novelDetail(novelId, reqAuth = true) {
    const url = `${this.hosts}/v2/novel/detail`;
    const params = { novel_id: novelId };
    const response = await this._request('GET', url, {}, params, null, reqAuth);
    return response.novel;
  }

  async novelNew(filter = "for_ios", maxNovelId = null, reqAuth = true) {
    const url = `${this.hosts}/v1/novel/new`;
    const params = { filter };
    if (maxNovelId) params.max_novel_id = maxNovelId;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  async novelFollow(restrict = "public", offset = null, reqAuth = true) {
    const url = `${this.hosts}/v1/novel/follow`;
    const params = { restrict };
    if (offset) params.offset = offset;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  async webviewNovel(novelId, raw = false, reqAuth = true) {
    const url = `${this.hosts}/webview/v2/novel`;
    const params = {
      id: novelId,
      viewer_version: "20221031_ai",
    };
    
    const response = await this._request('GET', url, {}, params, null, reqAuth);
    if (raw) return response;
    
    // Extract JSON content from HTML response
    const match = response.match(/novel:\s({.+}),\s+isOwnWork/);
    if (!match || match.length < 2) {
      throw new Error(`Extract novel content error: ${response}`);
    }
    
    try {
      return JSON.parse(match[1]);
    } catch (error) {
      throw new Error(`Failed to parse novel content: ${error.message}`);
    }
  }

  async novelText(novelId, reqAuth = true) {
    const novelData = await this.webviewNovel(novelId, false, reqAuth);
    novelData.novel_text = novelData.text;
    return novelData;
  }

  // Other Illust Methods

  async illustNew(contentType = "illust", filter = "for_ios", maxIllustId = null, reqAuth = true) {
    const url = `${this.hosts}/v1/illust/new`;
    const params = {
      content_type: contentType,
      filter,
    };
    if (maxIllustId) params.max_illust_id = maxIllustId;
    return this._request('GET', url, {}, params, null, reqAuth);
  }

  // Showcase Methods

  async showcaseArticle(showcaseId) {
    const url = "https://www.pixiv.net/ajax/showcase/article";
    const headers = {
      'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
      'Referer': 'https://www.pixiv.net',
    };
    const params = { article_id: showcaseId };
    return this._request('GET', url, headers, params, null, false);
  }
}
